package com.ww.springboot.boot.jvm.direct;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * java对于直接内存使用的测试类
 *
 * @author liuzuolong
 * @date 2022/6/29
 **/
@Slf4j
public class DirectBufferTest {


    private static final int SIZE_10MB = 10 * 1024 * 1024;


    public static void main(String[] args) throws InterruptedException {
        //读取和写入不同的文件,保证互不影响
        String filePath1 = "C:\\Users\\ZH500\\桌面\\test\\in\\ctms.zip";
        String filePath2 = "C:\\Users\\ZH500\\桌面\\test\\in\\ctms1.zip";
        String filePath3 = "C:\\Users\\ZH500\\桌面\\test\\in\\ctms2.zip";
        String toPath1 = "C:\\Users\\ZH500\\桌面\\test\\out\\ctms.zip";
        String toPath2 = "C:\\Users\\ZH500\\桌面\\test\\out\\ctms1.zip";
        String toPath3 = "C:\\Users\\ZH500\\桌面\\test\\out\\ctms2.zip";
        Integer fileByteLength = SIZE_10MB;
        //新建io读取文件的线程
        Thread commonIo = new Thread(() -> {
            commonIo(filePath1, fileByteLength, toPath1);
        });
        //新建nio使用直接内存读取文件的线程
        Thread nioWithDirectBuffer = new Thread(() -> {
            nioWithDirectBuffer(filePath2, fileByteLength, toPath2);
        });
        //新建nio使用堆内存读取文件的线程
        Thread nioWithHeapBuffer = new Thread(() -> {
            nioWithHeapBuffer(filePath3, fileByteLength, toPath3);
        });
        nioWithDirectBuffer.start();
        commonIo.start();
        nioWithHeapBuffer.start();
    }

    public static void commonIo(String filePath, Integer byteLength, String toPath) {
        //进行时间监控
        StopWatch ioTimeWatch = new StopWatch();
        ioTimeWatch.start("ioTimeWatch");
        try (FileInputStream fis = new FileInputStream(filePath);
             FileOutputStream fos = new FileOutputStream(toPath);
        ) {
            byte[] readByte = new byte[byteLength];
            int readCount = 0;
            while ((readCount = fis.read(readByte)) != -1) {
                // 读取了多少个字节，转换多少个。
                fos.write(readByte, 0, readCount);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        ioTimeWatch.stop();
        log.info(ioTimeWatch.prettyPrint());
    }


    /**
     * 直接内存 相当于是操作系统内存
     * 堆内存  是JVM规划的一片内存 要跟磁盘交互还要经过操作系统内存进行中转
     * 操作直接内存比操作堆内存少了一个交互环节
     * @param filePath
     * @param byteLength
     * @param toPath
     */
    public static void nioWithDirectBuffer(String filePath, Integer byteLength, String toPath) {
        StopWatch nioTimeWatch = new StopWatch();
        nioTimeWatch.start("nioDirectTimeWatch");
        try (FileChannel fci = new RandomAccessFile(filePath, "rw").getChannel();
             FileChannel fco = new RandomAccessFile(toPath, "rw").getChannel();
        ) {
            // 读写的缓冲区（分配一块儿直接内存）
            //要与allocate进行区分
            //进入到函数中
            ByteBuffer bb = ByteBuffer.allocateDirect(byteLength);
            while (true) {
                int len = fci.read(bb);
                if (len == -1) {
                    break;
                }
                bb.flip();
                fco.write(bb);
                bb.clear();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        nioTimeWatch.stop();
        log.info(nioTimeWatch.prettyPrint());
    }




    public static void nioWithHeapBuffer(String filePath, Integer byteLength, String toPath) {
        StopWatch nioTimeWatch = new StopWatch();
        nioTimeWatch.start("nioHeapTimeWatch");
        try (FileChannel fci = new RandomAccessFile(filePath, "rw").getChannel();
             FileChannel fco = new RandomAccessFile(toPath, "rw").getChannel();
        ) {
            // 读写的缓冲区（分配一块儿直接内存）
            //要与allocate进行区分
            ByteBuffer bb = ByteBuffer.allocate(byteLength);
            while (true) {
                int len = fci.read(bb);
                if (len == -1) {
                    break;
                }
                bb.flip();
                fco.write(bb);
                bb.clear();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        nioTimeWatch.stop();
        log.info(nioTimeWatch.prettyPrint());
    }
}